Multi-category dot-density maps often work well when the categories cluster geographically. Recent immigrants by country of origin work well for this. ## Data First we grab the immigrant data via cancensus, making use of the CensusMapper API tool to select the regions and variables we need.
#devtools::install_github("mountainmath/cancensus")
library(cancensus)
library(dotdensity)
library(tidyverse)
library(sf)
# options(cancensus.api_key='your_api_key')
regions=list(CMA="59933")
vectors_2011=c("v_CA11N_265","v_CA11N_268","v_CA11N_304","v_CA11N_334","v_CA11N_373","v_CA11N_376","v_CA11N_379","v_CA11N_382")
vectors=c("v_CA16_3636","v_CA16_3639","v_CA16_3669","v_CA16_3699","v_CA16_3741","v_CA16_3810","v_CA16_3750","v_CA16_3756","v_CA16_3783")
We choose the categories and colours we want to map and define a convenience function to rename the variables and compute the qantities for the other asian countries that we don’t break out.
categories=c("Americas","Europe","Africa + Oceania","Philippines","China","India","Other Asian Countries")
colors=c("#7a0177", "#3333cc", "#ff00ff", "#00ffff", "#ff1a1c", "#4dff4a", "#ffff33")
prep_data <- function(geo){
data <- geo %>% replace(is.na(.), 0)
data <- rename(data,
total=v_CA16_3636,
Americas=v_CA16_3639,
Europe=v_CA16_3669,
Philippines=v_CA16_3783,
China=v_CA16_3750,
India=v_CA16_3756)
data %>% mutate(`Other Asian Countries` = v_CA16_3741-Philippines-China-India,
`Africa + Oceania`=v_CA16_3699 + v_CA16_3810)
}
prep_data_2011 <- function(geo){
data <- geo %>% replace(is.na(.), 0)
data <- rename(data,
total=v_CA11N_265,
Americas=v_CA11N_268,
Europe=v_CA11N_304,
Africa=v_CA11N_334,
Philippines=v_CA11N_376,
China=v_CA11N_379,
India=v_CA11N_382)
data %>% mutate(`Other Asian Countries` = v_CA11N_373-Philippines-China-India)
}
Next we grab the data via cancensus,
data_csd=get_census(dataset = 'CA16', regions=regions,vectors=vectors,geo_format='sf',labels='short',level='CSD') %>% prep_data
data_ct=get_census(dataset = 'CA16', regions=regions,vectors=vectors,geo_format='sf',labels='short',level='CT') %>% prep_data
data_da=get_census(dataset = 'CA16', regions=regions,vectors=vectors,geo_format='sf',labels='short',level='DA') %>% prep_data
data_db=get_census(dataset = 'CA16', regions=regions,geo_format='sf',labels='short',level='DB')
which we then re-aggregate to make sure we don’t miss overall counts due to privacy cutoffs distribute them proportionally among the population.
# ct level data does not always align with CSD, so use care when doing this
data_ct <- dot_density.proportional_re_aggregate(data=data_ct,parent_data=data_csd,geo_match=setNames("GeoUID","CSD_UID"),categories=categories,base="Population")
data_da <- dot_density.proportional_re_aggregate(data=data_da,parent_data=data_ct,geo_match=setNames("GeoUID","CT_UID"),categories=categories,base="Population")
data_db <- dot_density.proportional_re_aggregate(data=data_db,parent_data=data_da,geo_match=setNames("GeoUID","DA_UID"),categories=categories,base="Population")
##Map All that’s left to do is to covert our re-aggregated block-level data to dots, using the dot_density.compute_dots function from the dotdensity package and feed it into the dot_density.dots_map function to add them to our basemap. As a scale we take one dot to represent 5 people.
scale=5 # 1 dot = 5 immigrants
basemap <- ggplot(data_csd) +
geom_sf(fill = base_color, size=0.1, color = 'grey') +
guides(colour = guide_legend(nrow=1,override.aes = list(size=15))) +
theme_opts +
scale_colour_manual(values = colors) +
labs(color = "", title="Immigrants 2011 - 2016",
caption="Source: StatCan Census 2016 via cancensus & CensusMapper.ca",
subtitle = paste0("1 dot = ",scale," people"))
To better understand the effect of the dot_density.proportional_reaggregate function we show the dot-density maps using data from different aggregation levels.
CSD level
dots <- dot_density.compute_dots(geo_data = data_csd, categories = categories, scale=scale) %>% st_as_sf
basemap +
geom_sf(data=dots,aes(color=Category),alpha=0.75,size=0.25,show.legend = "point") +
theme_opts

ggsave('../images/recent_immigrants_CSD.png',width=26,height=26)
CT Level Data
dots <- dot_density.compute_dots(geo_data = data_ct, categories = categories, scale=scale) %>% st_as_sf
basemap +
geom_sf(data=dots,aes(color=Category),alpha=0.75,size=0.25,show.legend = "point") +
theme_opts

ggsave('../images/recent_immigrants_CT.png',width=26,height=26)
DA Level Data
dots <- dot_density.compute_dots(geo_data = data_da, categories = categories, scale=scale) %>% st_as_sf
basemap +
geom_sf(data=dots,aes(color=Category),alpha=0.75,size=0.25,show.legend = "point") +
theme_opts

ggsave('../images/recent_immigrants_DA.png',width=26,height=26)
DB Level Data
dots <- dot_density.compute_dots(geo_data = data_db, categories = categories, scale=scale) %>% st_as_sf
basemap +
geom_sf(data=dots,aes(color=Category),alpha=0.75,size=0.25,show.legend = "point") +
theme_opts

ggsave('../images/recent_immigrants_DB.png',width=26,height=26)
LS0tCnRpdGxlOiAiUmVjZW50IEltbWlncmFudHMiCmF1dGhvcjogIkplbnMgdm9uIEJlcmdtYW5uIgpkYXRlOiAiMjAxNy0wOC0yNiIKb3V0cHV0OiBodG1sX25vdGVib29rCnZpZ25ldHRlOiA+CiAgJVxWaWduZXR0ZUluZGV4RW50cnl7UmVjZW50IEltbWlncmFudHN9CiAgJVxWaWduZXR0ZUVuZ2luZXtrbml0cjo6cm1hcmtkb3dufQogICVcVmlnbmV0dGVFbmNvZGluZ3tVVEYtOH0KLS0tCgpNdWx0aS1jYXRlZ29yeSBkb3QtZGVuc2l0eSBtYXBzIG9mdGVuIHdvcmsgd2VsbCB3aGVuIHRoZSBjYXRlZ29yaWVzIGNsdXN0ZXIgZ2VvZ3JhcGhpY2FsbHkuIFJlY2VudCBpbW1pZ3JhbnRzIGJ5CmNvdW50cnkgb2Ygb3JpZ2luIHdvcmsgd2VsbCBmb3IgdGhpcy4KIyMgRGF0YQpGaXJzdCB3ZSBncmFiIHRoZSBpbW1pZ3JhbnQgZGF0YSB2aWEgW2NhbmNlbnN1c10oaHR0cHM6Ly9naXRodWIuY29tL21vdW50YWluTWF0aC9jYW5jZW5zdXMpLCBtYWtpbmcgdXNlIG9mIHRoZSBbQ2Vuc3VzTWFwcGVyIEFQSSB0b29sXShodHRwczovL2NlbnN1c21hcHBlci5jYS9hcGkvQ0ExMSkgdG8gc2VsZWN0IHRoZSByZWdpb25zIGFuZCB2YXJpYWJsZXMgd2UgbmVlZC4KYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CiNkZXZ0b29sczo6aW5zdGFsbF9naXRodWIoIm1vdW50YWlubWF0aC9jYW5jZW5zdXMiKQpsaWJyYXJ5KGNhbmNlbnN1cykKbGlicmFyeShkb3RkZW5zaXR5KQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShzZikKIyBvcHRpb25zKGNhbmNlbnN1cy5hcGlfa2V5PSd5b3VyX2FwaV9rZXknKQpyZWdpb25zPWxpc3QoQ01BPSI1OTkzMyIpCnZlY3RvcnNfMjAxMT1jKCJ2X0NBMTFOXzI2NSIsInZfQ0ExMU5fMjY4Iiwidl9DQTExTl8zMDQiLCJ2X0NBMTFOXzMzNCIsInZfQ0ExMU5fMzczIiwidl9DQTExTl8zNzYiLCJ2X0NBMTFOXzM3OSIsInZfQ0ExMU5fMzgyIikKdmVjdG9ycz1jKCJ2X0NBMTZfMzYzNiIsInZfQ0ExNl8zNjM5Iiwidl9DQTE2XzM2NjkiLCJ2X0NBMTZfMzY5OSIsInZfQ0ExNl8zNzQxIiwidl9DQTE2XzM4MTAiLCJ2X0NBMTZfMzc1MCIsInZfQ0ExNl8zNzU2Iiwidl9DQTE2XzM3ODMiKQpgYGAKCldlIGNob29zZSB0aGUgY2F0ZWdvcmllcyBhbmQgY29sb3VycyB3ZSB3YW50IHRvIG1hcCBhbmQgZGVmaW5lIGEgY29udmVuaWVuY2UgZnVuY3Rpb24gdG8gcmVuYW1lIHRoZSB2YXJpYWJsZXMgYW5kIGNvbXB1dGUgdGhlIHFhbnRpdGllcyBmb3IgdGhlIG90aGVyIGFzaWFuIGNvdW50cmllcyB0aGF0IHdlIGRvbid0IGJyZWFrIG91dC4KYGBge3J9CmNhdGVnb3JpZXM9YygiQW1lcmljYXMiLCJFdXJvcGUiLCJBZnJpY2EgKyBPY2VhbmlhIiwiUGhpbGlwcGluZXMiLCJDaGluYSIsIkluZGlhIiwiT3RoZXIgQXNpYW4gQ291bnRyaWVzIikKY29sb3JzPWMoIiM3YTAxNzciLCAiIzMzMzNjYyIsICIjZmYwMGZmIiwgIiMwMGZmZmYiLCAiI2ZmMWExYyIsICIjNGRmZjRhIiwgIiNmZmZmMzMiKQoKcHJlcF9kYXRhIDwtIGZ1bmN0aW9uKGdlbyl7CiAgZGF0YSA8LSBnZW8gJT4lIHJlcGxhY2UoaXMubmEoLiksIDApCiAgZGF0YSA8LSByZW5hbWUoZGF0YSwKICAgIHRvdGFsPXZfQ0ExNl8zNjM2LAogICAgQW1lcmljYXM9dl9DQTE2XzM2MzksICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgRXVyb3BlPXZfQ0ExNl8zNjY5LAogICAgUGhpbGlwcGluZXM9dl9DQTE2XzM3ODMsCiAgICBDaGluYT12X0NBMTZfMzc1MCwKICAgIEluZGlhPXZfQ0ExNl8zNzU2KQogIGRhdGEgJT4lIG11dGF0ZShgT3RoZXIgQXNpYW4gQ291bnRyaWVzYCA9IHZfQ0ExNl8zNzQxLVBoaWxpcHBpbmVzLUNoaW5hLUluZGlhLAogICAgICAgICAgICAgICAgICAgICBgQWZyaWNhICsgT2NlYW5pYWA9dl9DQTE2XzM2OTkgKyB2X0NBMTZfMzgxMCkKfQpwcmVwX2RhdGFfMjAxMSA8LSBmdW5jdGlvbihnZW8pewogIGRhdGEgPC0gZ2VvICU+JSByZXBsYWNlKGlzLm5hKC4pLCAwKQogIGRhdGEgPC0gcmVuYW1lKGRhdGEsCiAgICB0b3RhbD12X0NBMTFOXzI2NSwKICAgIEFtZXJpY2FzPXZfQ0ExMU5fMjY4LCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgIEV1cm9wZT12X0NBMTFOXzMwNCwKICAgIEFmcmljYT12X0NBMTFOXzMzNCwKICAgIFBoaWxpcHBpbmVzPXZfQ0ExMU5fMzc2LAogICAgQ2hpbmE9dl9DQTExTl8zNzksCiAgICBJbmRpYT12X0NBMTFOXzM4MikKICBkYXRhICU+JSBtdXRhdGUoYE90aGVyIEFzaWFuIENvdW50cmllc2AgPSB2X0NBMTFOXzM3My1QaGlsaXBwaW5lcy1DaGluYS1JbmRpYSkKfQpgYGAKCgpOZXh0IHdlIGdyYWIgdGhlIGRhdGEgdmlhIGBjYW5jZW5zdXNgLApgYGB7ciwgZWNobz1UUlVFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpkYXRhX2NzZD1nZXRfY2Vuc3VzKGRhdGFzZXQgPSAnQ0ExNicsIHJlZ2lvbnM9cmVnaW9ucyx2ZWN0b3JzPXZlY3RvcnMsZ2VvX2Zvcm1hdD0nc2YnLGxhYmVscz0nc2hvcnQnLGxldmVsPSdDU0QnKSAlPiUgcHJlcF9kYXRhCmRhdGFfY3Q9Z2V0X2NlbnN1cyhkYXRhc2V0ID0gJ0NBMTYnLCByZWdpb25zPXJlZ2lvbnMsdmVjdG9ycz12ZWN0b3JzLGdlb19mb3JtYXQ9J3NmJyxsYWJlbHM9J3Nob3J0JyxsZXZlbD0nQ1QnKSAlPiUgcHJlcF9kYXRhCmRhdGFfZGE9Z2V0X2NlbnN1cyhkYXRhc2V0ID0gJ0NBMTYnLCByZWdpb25zPXJlZ2lvbnMsdmVjdG9ycz12ZWN0b3JzLGdlb19mb3JtYXQ9J3NmJyxsYWJlbHM9J3Nob3J0JyxsZXZlbD0nREEnKSAlPiUgcHJlcF9kYXRhCmRhdGFfZGI9Z2V0X2NlbnN1cyhkYXRhc2V0ID0gJ0NBMTYnLCByZWdpb25zPXJlZ2lvbnMsZ2VvX2Zvcm1hdD0nc2YnLGxhYmVscz0nc2hvcnQnLGxldmVsPSdEQicpCmBgYAoKd2hpY2ggd2UgdGhlbiByZS1hZ2dyZWdhdGUgdG8gbWFrZSBzdXJlIHdlIGRvbid0IG1pc3Mgb3ZlcmFsbCBjb3VudHMgZHVlIHRvIHByaXZhY3kgY3V0b2ZmcyBkaXN0cmlidXRlIHRoZW0KcHJvcG9ydGlvbmFsbHkgYW1vbmcgdGhlIHBvcHVsYXRpb24uCmBgYHtyfQojIGN0IGxldmVsIGRhdGEgZG9lcyBub3QgYWx3YXlzIGFsaWduIHdpdGggQ1NELCBzbyB1c2UgY2FyZSB3aGVuIGRvaW5nIHRoaXMKZGF0YV9jdCA8LSBkb3RfZGVuc2l0eS5wcm9wb3J0aW9uYWxfcmVfYWdncmVnYXRlKGRhdGE9ZGF0YV9jdCxwYXJlbnRfZGF0YT1kYXRhX2NzZCxnZW9fbWF0Y2g9c2V0TmFtZXMoIkdlb1VJRCIsIkNTRF9VSUQiKSxjYXRlZ29yaWVzPWNhdGVnb3JpZXMsYmFzZT0iUG9wdWxhdGlvbiIpCmRhdGFfZGEgPC0gZG90X2RlbnNpdHkucHJvcG9ydGlvbmFsX3JlX2FnZ3JlZ2F0ZShkYXRhPWRhdGFfZGEscGFyZW50X2RhdGE9ZGF0YV9jdCxnZW9fbWF0Y2g9c2V0TmFtZXMoIkdlb1VJRCIsIkNUX1VJRCIpLGNhdGVnb3JpZXM9Y2F0ZWdvcmllcyxiYXNlPSJQb3B1bGF0aW9uIikKZGF0YV9kYiA8LSBkb3RfZGVuc2l0eS5wcm9wb3J0aW9uYWxfcmVfYWdncmVnYXRlKGRhdGE9ZGF0YV9kYixwYXJlbnRfZGF0YT1kYXRhX2RhLGdlb19tYXRjaD1zZXROYW1lcygiR2VvVUlEIiwiREFfVUlEIiksY2F0ZWdvcmllcz1jYXRlZ29yaWVzLGJhc2U9IlBvcHVsYXRpb24iKQpgYGAKCgpgYGB7ciwgZWNobz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KbGlicmFyeShnZ3Bsb3QyKQpiZ19jb2xvcj0iIzExMTExMSIKYmFzZV9jb2xvcj0iIzMzMzMzMyIKdGV4dF9jb2xvcj0iI2VlZWVlZSIKdGhlbWVfb3B0czwtbGlzdCh0aGVtZShwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgICAgICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gYmdfY29sb3IsIGNvbG91ciA9IE5BKSwKICAgICAgICAgICAgICAgICAgICAgICBwbG90LmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbD1iZ19jb2xvciwgc2l6ZT0xLGxpbmV0eXBlPSJzb2xpZCIsY29sb3I9dGV4dF9jb2xvciksCiAgICAgICAgICAgICAgICAgICAgICAgYXhpcy5saW5lID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgICAgICAgIGF4aXMudGlja3MgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9NzAsaGp1c3QgPSAwLjUsIGNvbG9yPXRleHRfY29sb3IpLAogICAgICAgICAgICAgICAgICAgICAgIHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT01MCxoanVzdCA9IDAuNSwgY29sb3I9dGV4dF9jb2xvciksCiAgICAgICAgICAgICAgICAgICAgICAgcGxvdC5jYXB0aW9uID0gZWxlbWVudF90ZXh0KHNpemU9MjUsIGNvbG9yPXRleHRfY29sb3IpLAogICAgICAgICAgICAgICAgICAgICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTM1LCBjb2xvcj10ZXh0X2NvbG9yKSwKICAgICAgICAgICAgICAgICAgICAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplPTM1LCBjb2xvcj10ZXh0X2NvbG9yKSwKICAgICAgICAgICAgICAgICAgICAgICBsZWdlbmQuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsPWJnX2NvbG9yLCBzaXplPTEsbGluZXR5cGU9InNvbGlkIixjb2xvcj1iZ19jb2xvciksCiAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kLmtleSA9IGVsZW1lbnRfcmVjdChmaWxsID0gYmdfY29sb3IsY29sb3IgPSBiZ19jb2xvciksCiAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kLmtleS53aWR0aCA9IHVuaXQoMywgJ2xpbmVzJyksCiAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpLAogICAgICAgICAgICAgICAgIGNvb3JkX3NmKHhsaW09YygtMTIzLjI5LC0xMjIuNiksIHlsaW09Yyg0OS4wMiw0OS4zNSkpICAjIHpvb20gaW4gYSBiaXQKKQoKCmBgYAoKCiMjTWFwCkFsbCB0aGF0J3MgbGVmdCB0byBkbyBpcyB0byBjb3ZlcnQgb3VyIHJlLWFnZ3JlZ2F0ZWQgYmxvY2stbGV2ZWwgZGF0YSB0byBkb3RzLCB1c2luZyB0aGUgYGRvdF9kZW5zaXR5LmNvbXB1dGVfZG90c2AKZnVuY3Rpb24gZnJvbSB0aGUgW2Bkb3RkZW5zaXR5YCBwYWNrYWdlXSgpIGFuZCBmZWVkIGl0IGludG8gdGhlIGBkb3RfZGVuc2l0eS5kb3RzX21hcGAgZnVuY3Rpb24gdG8gYWRkIHRoZW0gdG8Kb3VyIGJhc2VtYXAuIEFzIGEgc2NhbGUgd2UgdGFrZSBvbmUgZG90IHRvIHJlcHJlc2VudCA1IHBlb3BsZS4KYGBge3J9CnNjYWxlPTUgIyAxIGRvdCA9IDUgaW1taWdyYW50cwoKYmFzZW1hcCA8LSAgIGdncGxvdChkYXRhX2NzZCkgKwogIGdlb21fc2YoZmlsbCA9IGJhc2VfY29sb3IsIHNpemU9MC4xLCBjb2xvciA9ICdncmV5JykgKwogIGd1aWRlcyhjb2xvdXIgPSBndWlkZV9sZWdlbmQobnJvdz0xLG92ZXJyaWRlLmFlcyA9IGxpc3Qoc2l6ZT0xNSkpKSArCiAgdGhlbWVfb3B0cyArCiAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSBjb2xvcnMpICsKICBsYWJzKGNvbG9yID0gIiIsIHRpdGxlPSJJbW1pZ3JhbnRzIDIwMTEgLSAyMDE2IiwKICAgICAgIGNhcHRpb249IlNvdXJjZTogU3RhdENhbiBDZW5zdXMgMjAxNiB2aWEgY2FuY2Vuc3VzICYgQ2Vuc3VzTWFwcGVyLmNhIiwKICAgICAgIHN1YnRpdGxlID0gcGFzdGUwKCIxIGRvdCA9ICIsc2NhbGUsIiBwZW9wbGUiKSkKYGBgCgoKVG8gYmV0dGVyIHVuZGVyc3RhbmQgdGhlIGVmZmVjdCBvZiB0aGUgKipkb3RfZGVuc2l0eS5wcm9wb3J0aW9uYWxfcmVhZ2dyZWdhdGUqKiBmdW5jdGlvbiB3ZSBzaG93IHRoZSBkb3QtZGVuc2l0eSBtYXBzIHVzaW5nIGRhdGEgZnJvbSBkaWZmZXJlbnQgYWdncmVnYXRpb24gbGV2ZWxzLgoKIyMjIENTRCBsZXZlbApgYGB7ciwgZmlnLmhlaWdodD0xNCwgZmlnLndpZHRoPTE0LCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpkb3RzIDwtIGRvdF9kZW5zaXR5LmNvbXB1dGVfZG90cyhnZW9fZGF0YSA9IGRhdGFfY3NkLCBjYXRlZ29yaWVzID0gY2F0ZWdvcmllcywgc2NhbGU9c2NhbGUpICU+JSBzdF9hc19zZgpiYXNlbWFwICsKICBnZW9tX3NmKGRhdGE9ZG90cyxhZXMoY29sb3I9Q2F0ZWdvcnkpLGFscGhhPTAuNzUsc2l6ZT0wLjI1LHNob3cubGVnZW5kID0gInBvaW50IikgKwogIHRoZW1lX29wdHMKCmdnc2F2ZSgnLi4vaW1hZ2VzL3JlY2VudF9pbW1pZ3JhbnRzX0NTRC5wbmcnLHdpZHRoPTI2LGhlaWdodD0yNikKYGBgCgojIyMgQ1QgTGV2ZWwgRGF0YQpgYGB7ciwgZmlnLmhlaWdodD0xNCwgZmlnLndpZHRoPTE0LCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpkb3RzIDwtIGRvdF9kZW5zaXR5LmNvbXB1dGVfZG90cyhnZW9fZGF0YSA9IGRhdGFfY3QsIGNhdGVnb3JpZXMgPSBjYXRlZ29yaWVzLCBzY2FsZT1zY2FsZSkgJT4lIHN0X2FzX3NmCmJhc2VtYXAgKwogIGdlb21fc2YoZGF0YT1kb3RzLGFlcyhjb2xvcj1DYXRlZ29yeSksYWxwaGE9MC43NSxzaXplPTAuMjUsc2hvdy5sZWdlbmQgPSAicG9pbnQiKSArCiAgdGhlbWVfb3B0cwoKZ2dzYXZlKCcuLi9pbWFnZXMvcmVjZW50X2ltbWlncmFudHNfQ1QucG5nJyx3aWR0aD0yNixoZWlnaHQ9MjYpCmBgYAoKCgojIyMgREEgTGV2ZWwgRGF0YQpgYGB7ciwgZmlnLmhlaWdodD0xNCwgZmlnLndpZHRoPTE0LCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpkb3RzIDwtIGRvdF9kZW5zaXR5LmNvbXB1dGVfZG90cyhnZW9fZGF0YSA9IGRhdGFfZGEsIGNhdGVnb3JpZXMgPSBjYXRlZ29yaWVzLCBzY2FsZT1zY2FsZSkgJT4lIHN0X2FzX3NmCmJhc2VtYXAgKwogIGdlb21fc2YoZGF0YT1kb3RzLGFlcyhjb2xvcj1DYXRlZ29yeSksYWxwaGE9MC43NSxzaXplPTAuMjUsc2hvdy5sZWdlbmQgPSAicG9pbnQiKSArCiAgdGhlbWVfb3B0cwoKZ2dzYXZlKCcuLi9pbWFnZXMvcmVjZW50X2ltbWlncmFudHNfREEucG5nJyx3aWR0aD0yNixoZWlnaHQ9MjYpCmBgYAoKCiMjIyBEQiBMZXZlbCBEYXRhCmBgYHtyLCBmaWcuaGVpZ2h0PTE0LCBmaWcud2lkdGg9MTQsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmRvdHMgPC0gZG90X2RlbnNpdHkuY29tcHV0ZV9kb3RzKGdlb19kYXRhID0gZGF0YV9kYiwgY2F0ZWdvcmllcyA9IGNhdGVnb3JpZXMsIHNjYWxlPXNjYWxlKSAlPiUgc3RfYXNfc2YKYmFzZW1hcCArCiAgZ2VvbV9zZihkYXRhPWRvdHMsYWVzKGNvbG9yPUNhdGVnb3J5KSxhbHBoYT0wLjc1LHNpemU9MC4yNSxzaG93LmxlZ2VuZCA9ICJwb2ludCIpICsKICB0aGVtZV9vcHRzCgpnZ3NhdmUoJy4uL2ltYWdlcy9yZWNlbnRfaW1taWdyYW50c19EQi5wbmcnLHdpZHRoPTI2LGhlaWdodD0yNikKYGBgCgoK